﻿using System;
using SCG = System.Collections.Generic;

using Nemerle;
using Nemerle.Assertions;
using Nemerle.Compiler;
using Nemerle.Collections;
using Nemerle.Utility;
using PT = Nemerle.Compiler.Parsetree;
using TT = Nemerle.Compiler.Typedtree;

namespace Nemerle.OpenCL {

    macro BufferedCall(func : expr)
        syntax("buffered", func)
    {
        match(func) {
            | PT.PExpr.Call(func, args) => BufferedCallImpl.MakeBufferedCall(func, NList.ToList(args), false)
            | _ => Message.FatalError("Improper usage of BufferedCall macro.")
        }
    }

    macro BufferedUnsafeCall(func : expr)
        syntax("buffered_unsafe", func)
    {
        match(func) {
            | PT.PExpr.Call(func, args) => BufferedCallImpl.MakeBufferedCall(func, NList.ToList(args), true)
            | _ => Message.FatalError("Improper usage of BufferedUnsafeCall macro.")
        }
    }

    module BufferedCallImpl {

        public MakeBufferedCall(func : PT.PExpr, args : list[PT.PExpr], unsafe : bool) : PT.PExpr {
            if(unsafe) {
                // use PinObject
                def args1 = args + [ <[ 0 ]>, <[ IntPtr.Zero ]>, <[ ref buffer_size ]> ];
                def args2 = args + [ <[ buffer_size ]>, <[ buffer_ptr ]>, <[ ref buffer_size ]> ];
                <[
                    mutable buffer_size;
                    CheckError($func(..$args1));
                    def buffer = array(buffer_size :> int);
                    PinObject(buffer_ptr = buffer, CheckError($func(..$args2)));
                    buffer
                ]>
            } else {
                // don't use PinObject
                def args1 = args + [ <[ 0 ]>, <[ null ]>, <[ ref buffer_size ]> ];
                def args2 = args + [ <[ buffer_size ]>, <[ buffer ]>, <[ ref buffer_size ]> ];
                <[
                    mutable buffer_size;
                    CheckError($func(..$args1));
                    def buffer = array(buffer_size :> int);
                    CheckError($func(..$args2));
                    buffer
                ]>
            }
        }

    }

}